package com.onpositive.simple.persistance;

import java.util.ArrayList;
import java.util.Iterator;
import java.lang.reflect.*;


class UniversalObjectStore<T> extends AbstractObjectStore<T> {

	protected SearchIndex<?> searchIndexArray[];
	protected Field fields[];
	protected int fieldsCount;

	public UniversalObjectStore( Class<T> clazz, RecordStorage recordStorage, IStringStorage stringStorage, String storageType , String path ) {
		super( clazz, recordStorage, stringStorage, storageType, path );

		ArrayList<Field> temporaryFieldsArray = new ArrayList<Field>();

		for (Class<?> klacc = objectClass; klacc != null; klacc = klacc
				.getSuperclass()) {
			Field fld[] = klacc.getDeclaredFields();
			for (int i = 0; i < fld.length; i++)
				temporaryFieldsArray.add(fld[i]);
		}

		fieldsCount = temporaryFieldsArray.size();
		recordStorage.recordLength = fieldsCount;
		recordStorage.init() ;

		fields = new Field[fieldsCount];
		for (int i = 0; i < fieldsCount; i++) {
			fields[i] = temporaryFieldsArray.get(i);
			fields[i].setAccessible(true);
		}

		searchIndexArray = new SearchIndex[fieldsCount];
		for (int i = 0; i < fieldsCount; i++)
			searchIndexArray[i] = SearchIndexFactory.constructSearchIndex( fields[i].getType(), this );
	}

	@Override
	public Object put(T object) {

		int record[] = this.constructRecord(object);
		int id = recordStorage.putRecord(record);
		submitToIndeces(record, id);
		return id;
	}

	@Override
	public T get(Object id) throws IllegalArgumentException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void delete(Object id) throws IllegalArgumentException {
		// TODO Auto-generated method stub

	}

	@Override
	public Object find(T source) throws IllegalArgumentException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int[] query(String sortField, boolean askEndingSort,
			Filter... filters) {
		int fieldIndex = 0;
		for (; fieldIndex < fields.length; fieldIndex++)
			if (filters[0].field == fields[fieldIndex].getName())
				break;

		if (fieldIndex == fields.length)
			return null ;

		
		boolean requiresSubinterval = false;
		boolean includeLowerBound   = false;
		boolean includeUpperBound   = false;
		Object lowerBound  = null;
		Object upperBound  = null;
		Object targetValue = null;
		
		ExtractBoundsParameter ebp = new ExtractBoundsParameter(fieldIndex, lowerBound, upperBound, targetValue, requiresSubinterval, includeLowerBound,
				includeUpperBound, filters) ;

		extractBounds( ebp );
		
		Class indexClass = searchIndexArray[fieldIndex].indexClass ; 
		int[] serachResultIdArray = searchIndexArray[fieldIndex].query( ebp.requiresSubinterval, ebp.includeLowerBound,	ebp.includeUpperBound,
				ebp.lowerBound, ebp.upperBound, ebp.targetValue ) ;
		
		return serachResultIdArray ;
	}

	private void extractBounds(ExtractBoundsParameter parameterObject)
	{		

		if ((parameterObject.filters.length == 1) && (parameterObject.filters[0].kind == Filter.EQ))
			 parameterObject.targetValue = parameterObject.filters[0].bound;
		
		else {
			parameterObject.requiresSubinterval = true;

			if (fields[parameterObject.fieldIndex].getType() == String.class)
			{
				for (int i = 0; i < parameterObject.filters.length; i++)
				{
					String bound = (String)parameterObject.filters[i].bound ;
					switch (parameterObject.filters[i].kind)
					{					
					case Filter.GT: {
						if ( parameterObject.lowerBound == null) {
							 parameterObject.lowerBound = bound ;
							parameterObject.includeLowerBound = false;
						}
						else {
							if( bound.compareTo( (String) parameterObject.lowerBound) < 0 )
							{
								 parameterObject.lowerBound = bound ;
								parameterObject.includeLowerBound = false ;
							}								
						}
						break ;
					}
					case Filter.GE: {
						if ( parameterObject.lowerBound == null) {
							 parameterObject.lowerBound = bound ;
							parameterObject.includeLowerBound = true;
						}
						else {
							if( bound.compareTo( (String) parameterObject.lowerBound) <= 0 )
							{
								 parameterObject.lowerBound = bound ;
								parameterObject.includeLowerBound = true ;
							}								
						}
						break ;
					}
					case Filter.LT: {
						if ( parameterObject.upperBound == null) {
							 parameterObject.upperBound = bound ;
							parameterObject.includeLowerBound = false;
						}
						else {
							if( bound.compareTo( (String) parameterObject.upperBound) > 0 )
							{
								 parameterObject.upperBound = bound ;
								parameterObject.includeUpperBound = false ;
							}
						}
						break ;
					}
					case Filter.LE: {
						if ( parameterObject.upperBound == null) {
							 parameterObject.upperBound = bound ;
							parameterObject.includeLowerBound = true;
						}
						else {
							if( bound.compareTo( (String) parameterObject.upperBound) >= 0 )
							{
								 parameterObject.upperBound = bound ;
								parameterObject.includeUpperBound = true ;
							}
						}
						break ;
					}

					}
				}
			}

			if ((fields[parameterObject.fieldIndex].getType() == int.class)	|| (fields[parameterObject.fieldIndex].getType() == Integer.class)) {
				for (int i = 0; i < parameterObject.filters.length; i++) {
					int bound = (Integer)parameterObject.filters[i].bound ;
					switch (parameterObject.filters[i].kind)
					{					
					case Filter.GT: {
						if ( parameterObject.lowerBound == null) {
							 parameterObject.lowerBound = bound ;
							parameterObject.includeLowerBound = false;
						}
						else {
							if( bound < (Integer) parameterObject.lowerBound )
							{
								 parameterObject.lowerBound = bound ;
								parameterObject.includeLowerBound = false ;
							}								
						}
						break ;
					}
					case Filter.GE: {
						if ( parameterObject.lowerBound == null) {
							 parameterObject.lowerBound = bound ;
							parameterObject.includeLowerBound = true;
						}
						else {
							if( bound <= (Integer) parameterObject.lowerBound )
							{
								 parameterObject.lowerBound = bound ;
								parameterObject.includeLowerBound = true ;
							}								
						}
						break ;
					}
					case Filter.LT: {
						if ( parameterObject.upperBound == null) {
							 parameterObject.upperBound = bound ;
							parameterObject.includeLowerBound = false;
						}
						else {
							if( bound > (Integer) parameterObject.upperBound )
							{
								 parameterObject.upperBound = bound ;
								parameterObject.includeUpperBound = false ;
							}
						}
						break ;
					}
					case Filter.LE: {
						if ( parameterObject.upperBound == null) {
							 parameterObject.upperBound = bound ;
							parameterObject.includeUpperBound = true;
						}
						else {
							if( bound >= (Integer) parameterObject.upperBound )
							{
								parameterObject.upperBound = bound ;
								parameterObject.includeUpperBound = true ;
							}
						}
						break ;
					}

					}
				}

				}

		}
	}

	public Object findByInt(int key) {

		return ((IntegerIndex) searchIndexArray[0]).findByKey(key);
	}
	public Object findByString( String key) {

		return ((StringIndex) searchIndexArray[1]).findByKey(key);
	}

	@Override
	public Iterator<T> iterator() {
		// TODO Auto-generated method stub
		return null;
	}

	protected int[] constructRecord(T object) {

		int record[] = new int[fieldsCount];

		try {
			for (int i = 0; i < fieldsCount; i++) {
				Class<?> fieldClass = fields[i].getType();
				if (fieldClass == String.class) {
					record[i] = stringStorage.put((String) fields[i]
							.get(object));
					continue;
				}
				if ((fieldClass == Boolean.class)
						|| (fieldClass == boolean.class)) {
					boolean fieldValueIsTrue = (Boolean) fields[i].get(object);
					record[i] = fieldValueIsTrue ? 1 : 0;
					continue;
				}
				record[i] = (Integer) fields[i].get(object);
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return record;
	}

	protected void submitToIndeces(int record[], int id) {
		for (int i = 0; i < fieldsCount; i++)
			searchIndexArray[i].submitObject(record[i], id);

	}
}
